package com.e9ab98e991ab.librepository.api;



import com.e9ab98e991ab.librepository.tools.ValueLiveData;
import androidx.annotation.MainThread;
import androidx.annotation.WorkerThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;

/**
 * 网络层
 * @author gaoxin 18-11-7 上午11:19
 * @version V1.0.0
 * @name NetworkBoundResource
 */
public abstract class NetworkBoundResource<ResultType,RequestType> {
    private MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<Resource<ResultType>>();
    private AppExecutors appExecutors;
    private LiveData<RequestType> valueLiveData = ValueLiveData.create(null);
    @MainThread
    public NetworkBoundResource(AppExecutors appExecutors){
        this.appExecutors = appExecutors;
        result.setValue(Resource.loading(null));

        LiveData<ResultType> dbSource = loadFromDb();

        result.addSource(dbSource, data -> {
            result.removeSource(dbSource);
            if (shouldFetch(data)){
                fetchFromNetwork(dbSource);
            }else {
                result.addSource(dbSource,newData ->{
                    setValue(Resource.success(newData));
                });
            }
        });
    }

    @MainThread
    private void setValue(Resource<ResultType> newValue) {
        if (result.getValue() != newValue) {
            result.setValue(newValue);
        }
    }

    private void fetchFromNetwork(LiveData<ResultType> dbSource) {
        LiveData<ApiResponse<RequestType>> apiResponse = createCall();
        // we re-attach dbSource as a new source, it will dispatch its latest value quickly
        result.addSource(dbSource, newData ->{
           setValue(Resource.loading(newData));
        });

        result.addSource(apiResponse,response -> {
            result.removeSource(apiResponse);
            result.removeSource(dbSource);

            if (response instanceof ApiSuccessResponse){
                appExecutors.diskIO().execute(()->{
                    RequestType requestType = processResponse((ApiSuccessResponse<RequestType>)response);
                    saveCallResult(requestType);

                    appExecutors.mainThread().execute(()->{
                        valueLiveData = ValueLiveData.create(requestType);
                        result.addSource(loadFromDb(),newData ->{
                            setValue(Resource.success(newData));
                        });
                    });
                });
            }else if (response instanceof ApiEmptyResponse){
                appExecutors.mainThread().execute(()->{
                    result.addSource(loadFromDb(),newData->{
                        setValue(Resource.success(newData));
                    });
                });
            }else if (response instanceof ApiErrorResponse){
                result.addSource(dbSource,newData->{
                    setValue(Resource.error(((ApiErrorResponse) response).getErrorMessage(),newData));
                });
            }
        });
    }


    public LiveData<Resource<ResultType>> asLiveData() {
        return result;
    }

    public LiveData<RequestType> getValueLiveData() {
        return valueLiveData;
    }


    @WorkerThread
    protected  RequestType processResponse(ApiSuccessResponse<RequestType> response){
        return response.getBody();
    }


    // 当要把网络数据存储到数据库中时调用
    protected abstract void saveCallResult(RequestType item);

    // 决定是否去网络获取数据
    @MainThread
    protected abstract boolean shouldFetch(ResultType data);

    // 用于从数据库中获取缓存数据
    @MainThread
    protected abstract LiveData<ResultType> loadFromDb();


    // 创建网络数据请求
    @MainThread
    protected abstract LiveData<ApiResponse<RequestType>> createCall();




}
